/* vi:set ts=4 sts=4 sw=4 foldmethod=marker:
 *
 * VIM - Vi IMproved        by Bram Moolenaar
 *              GUI/Motif support by Robert Webb
 *              Macintosh port by Dany St-Amant
 *                        and Axel Kielhorn
 *              Port to MPW by Bernhard Pruemmer
 *              Initial Carbon port by Ammon Skidmore
 *              Initial Cocoa port by Jjgod Jiang
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 * 
 * TODO: 
 * 
 * 1. Special key input.
 * 2. Scrollbars.
 * 3. Toolbar.
 * 4. delete_lines and insert_lines.
 * 5. Marked text.
 */

#include "vim.h"
#import <Cocoa/Cocoa.h>
#import "mac/VimApplication.h"

/* Internal Data Structures {{{ */

/* Key mapping {{{2 */

static struct
{
    UniChar function_key;
    int     vim_key;
} function_key_mapping[] =
{
    { NSUpArrowFunctionKey,     K_UP    },
    { NSDownArrowFunctionKey,   K_DOWN  },
    { NSLeftArrowFunctionKey,   K_LEFT  },
    { NSRightArrowFunctionKey,  K_RIGHT },

    { NSF1FunctionKey,          K_F1    },
    { NSF2FunctionKey,          K_F2    },
    { NSF3FunctionKey,          K_F3    },
    { NSF4FunctionKey,          K_F4    },
    { NSF5FunctionKey,          K_F5    },
    { NSF6FunctionKey,          K_F6    },
    { NSF7FunctionKey,          K_F7    },
    { NSF8FunctionKey,          K_F8    },
    { NSF9FunctionKey,          K_F9    },
    { NSF10FunctionKey,         K_F10   },

    { NSF11FunctionKey,         K_F11   },
    { NSF12FunctionKey,         K_F12   },
    { NSF13FunctionKey,         K_F13   },
    { NSF14FunctionKey,         K_F14   },
    { NSF15FunctionKey,         K_F15   },

    { NSInsertFunctionKey,      K_INS   },
    { NSDeleteFunctionKey,      K_DEL   },
    { NSHomeFunctionKey,        K_HOME  },
    { NSEndFunctionKey,         K_END   },

    { NSPageUpFunctionKey,      K_PAGEUP    },
    { NSPageDownFunctionKey,    K_PAGEDOWN  },
    
    { '\t',     '\t'    },  /* tab */
    { '\r',     '\r'    },  /* return */
    { '\003',   '\003'  },  /* enter */
    { '\031',   '\031'  },  /* backtab */
    { '\033',   '\033'  },  /* escape */ 
    { '\177',   K_BS    },  /* backspace */

    /* End of list marker: */
    { 0, 0 }, 
};

/* 2}}} */

#define VIM_MAX_FONT_NAME_LEN   256
#define VIM_DEFAULT_FONT_SIZE   9
#define VIM_DEFAULT_FONT_NAME   (char_u *) "Monaco:h9"

#define run_app()           [NSApp setRunning: YES]
#define stop_app()          [NSApp setRunning: NO]
#define app_is_running()    ([NSApp running] == YES)

/* Data Structures }}}*/

/* Internal functions prototypes {{{ */

int hex_digit(int c);
NSColor *NSColorFromGuiColor(guicolor_T color, float alpha);

GuiFont find_font(char_u *font_name);
int points_to_pixels(char_u *str, char_u **end);
NSFont *get_font(char_u *font_name, int size);

/* Internal functions prototypes }}} */

/* Initializtion and Finalization {{{ */

int gui_mch_init()
{
    fprintf(stderr, "gui_mch_init\n");

    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    [VIMApplication sharedApplication];
    
    [NSApp setDelegate: NSApp];
    [NSApp createApplicationMenu];
    [NSApp createWindow];

    // Set values so that pixels and characters are in one-to-one
    // correspondence (assuming all characters have the same dimensions).
    gui.scrollbar_width = gui.scrollbar_height = 0;

    gui.char_height = 1;
    gui.char_width = 1;
    gui.char_ascent = 0;

    gui.def_norm_pixel = gui.norm_pixel = 0;
    gui.def_back_pixel = gui.back_pixel = 0xFFFFFF;

    [NSApp setDefaultColorsBackground: (int) gui.back_pixel
                           foreground: (int) gui.norm_pixel];
    [NSApp setBackgroundColor: (int) gui.back_pixel];
    [NSApp setForegroundColor: (int) gui.norm_pixel];

    // NOTE: If this call is left out the cursor is opaque.
    highlight_gui_started();
    
    return OK;
}

int gui_mch_init_check()
{
    fprintf(stderr, "gui_mch_init_check\n");

    /* see main.c for reason to disallow */
    if (disallow_gui) 
        return FAIL;

    return OK;
}

void gui_mch_exit(int rc)
{
    fprintf(stderr, "gui_mch_exit\n");

    exit(rc);
}

int gui_mch_open()
{
    fprintf(stderr, "gui_mch_open\n");
    
    if (gui_win_x != -1 && gui_win_y != -1)
        gui_mch_set_winpos(gui_win_x, gui_win_y);

    return OK;
}

void gui_mch_prepare(int *argc, char **argv)
{
    fprintf(stderr, "gui_mch_prepare\n");
}

void gui_mch_set_shellsize(
    int width,
    int height,
    int min_width,
    int min_height,
    int base_width,
    int base_height,
    int direction)
{    
    NSLog(@"gui_mch_set_shellsize: "
          "(%d, %d, %d, %d, %d, %d, %d)\n", 
            width, height, min_width, min_height, 
            base_width, base_height, direction);
            
    fprintf(stderr, "gui.num_rows (%d) * gui.char_height (%d) = %d\n", 
            gui.num_rows, gui.char_height, gui.num_rows * gui.char_height);

    fprintf(stderr, "gui.num_cols (%d) * gui.char_width (%d) = %d\n", 
            gui.num_cols, gui.char_width, gui.num_cols * gui.char_width);
}

void gui_mch_set_text_area_pos(int x, int y, int w, int h)
{
    fprintf(stderr, "gui_mch_set_text_area_pos: "
            "%d, %d, %d, %d\n", x, y, w, h);
}

/* Initializtion and Finalization }}} */

/* Event related {{{ */

void gui_mch_update()
{
    gui_mch_wait_for_chars(0);
}

/* wtime < 0: wait forever
 * wtime > 0: wait wtime milliseconds
 * wtime = 0: don't wait, only poll existing events
 */
int gui_mch_wait_for_chars(int wtime)
{
    NSEvent *event;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSDate  *date;
    static   int first_time = 1;

    [NSApp flushDrawing];
    // NSLog(@"gui_mch_wait_for_chars: %d", wtime);
    
    if (wtime == 0)
    {
        // NSLog(@"gui_mch_wait_for_chars: don't wait");
        date = [NSDate distantPast];
    } 
    else if (wtime > 0)
    {
        // NSLog(@"gui_mch_wait_for_chars: wait for %d ms", wtime);
        date = [NSDate dateWithTimeIntervalSinceNow: (double) wtime / 1000.0];
    }
    /* wtime < 0, wait forever */
    else
    {
        // NSLog(@"gui_mch_wait_for_chars: wait forever");
        date = [NSDate distantFuture];
    }
    
    /* It's tricky here: we don't want to use -[NSApplication run:] 
     * all the time, but we need it to do some initialization for
     * the first time this app starts. So we install a timer to 
     * stop: NSApp just after it runs.
     */
    if (first_time)
    {
        NSLog(@"first time, begin initialization...");
        first_time = 0;
        [NSTimer scheduledTimerWithTimeInterval: 0.0
                                         target: NSApp
                                       selector: @selector(initializeApplicationTimer:)
                                       userInfo: 0
                                        repeats: NO];
        [NSApp run];
        NSLog(@"end initialization.");
    }

    run_app();
    while (app_is_running() && 
           (event = [NSApp nextEventMatchingMask: NSAnyEventMask
                                       untilDate: date
                                          inMode: NSDefaultRunLoopMode
                                         dequeue: YES]) != nil)
    {
        [NSApp sendEvent: event];
    }

    [pool release];
    return OK;
}

/* Event related }}} */

/* Input Method Handling {{{ */

int im_get_status()
{
    if (! gui.in_use)
            return 0;

    return 0;
}

void im_set_active(int active)
{
}

void im_set_position(int row, int col)
{
}

/* Input Method Handling }}} */

/* Misc Stuff {{{ */

void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
{
    CGRect rect;

    rect = CGDisplayBounds(CGMainDisplayID());

    *screen_w = (int) rect.size.width;
    *screen_h = (int) rect.size.height;

    fprintf(stderr, "gui_mch_get_screen_dimensions: %d, %d\n", 
            *screen_w, *screen_h);
}

#ifdef USE_MCH_ERRMSG

void display_errors()
{
    fprintf(stderr, "display_errors\n");
    fflush(stderr);
}

#endif

int gui_mch_haskey(char_u *name)
{
    return OK;
}

void gui_mch_beep()
{
    NSBeep();
}

void gui_mch_toggle_tearoffs(int enable)
{
    /* no tearoff menus */
}

/* Misc Stuff }}} */

/* Font Handling {{{ */

int gui_mch_init_font(char_u *font_name, int fontset)
{
    NSAutoreleasePool *pool;
    NSFont  *mac_font;
    GuiFont  vim_font;
    NSSize   advance;
    char_u   used_font_name[VIM_MAX_FONT_NAME_LEN];

    if (font_name == NULL)
        font_name = VIM_DEFAULT_FONT_NAME;

    fprintf(stderr, "gui_mch_init_font: %s\n", font_name);

    if (STRCMP(font_name, "*") == 0)
        return FAIL;

    pool = [NSAutoreleasePool new];
    vim_font = find_font(font_name);

    if (vim_font == NOFONT)
    {
        NSLog(@"find_font failed\n");
        return FAIL;
    }

    gui.norm_font = vim_font;

    vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
    hl_set_font_name(used_font_name);

    mac_font = (NSFont *) vim_font;
    advance  = [mac_font maximumAdvancement];

    /* in 72 DPI, 1 point = 1 pixel */
    gui.char_ascent = roundf([mac_font ascender]);
    gui.char_width  = roundf(advance.width);
    gui.char_height = roundf([mac_font ascender] - [mac_font descender]) + p_linespace;
        
    fprintf(stderr, "ascent = %d, width = %d, height = %d, %f, %f\n", 
            gui.char_ascent, gui.char_width, gui.char_height, 
            [mac_font ascender], [mac_font descender]);

    [pool release];

    [[NSApp textStorage] setFont: mac_font];

    return OK;
}

void gui_mch_free_font(GuiFont font)
{
    [(NSFont *) font release];
}

void gui_mch_set_font(GuiFont font)
{
}

GuiFont gui_mch_get_font(char_u *name, int giveErrorIfMissing)
{
    GuiFont font;

    fprintf(stderr, "gui_mch_get_font: %s\n", name);
    font = find_font(name);

    if (font == NOFONT)
    {
	    if (giveErrorIfMissing)
    	    EMSG2(_(e_font), name);
    	return NOFONT;
    }
    /*
     * TODO : Accept only monospace
     */

    return font;
}

char_u *gui_mch_get_fontname(GuiFont font, char_u *name)
{
    if (name == NULL)
        return NULL;

    return vim_strsave(name);
}

int gui_mch_adjust_charheight()
{
    return OK;
}

/* Font Handling }}} */

/* Window Handling {{{ */

void gui_mch_set_foreground()
{
    fprintf(stderr, "gui_mch_set_foreground\n");
}

void gui_mch_set_winpos(int x, int y)
{
    fprintf(stderr, "gui_mch_set_winpos: %d, %d\n", x, y);
}

int gui_mch_get_winpos(int *x, int *y)
{
    return OK;
}

void gui_mch_settitle(char_u *title, char_u *icon)
{
    fprintf(stderr, "gui_mch_set_title: (%s, %s)\n", title, icon);
}

void gui_mch_iconify()
{
    fprintf(stderr, "gui_mch_iconify\n");
}

/* Window Handling }}} */

/* Menu Handling {{{ */

/* TODO: 
 *
 * 1. popup menus are ignored. 
 * 2. command ID has not been assigned.
 */
 
NSMenuItem *insert_menu_item(vimmenu_T *menu)
{
    vimmenu_T  *parent, *brother;
    NSMenu     *parent_menu;
    NSMenuItem *mac_menu_item, *item;
    int         alloc = 0, index;

    brother = menu->next;
    /* My brother could be the PopUp, find my real brother */
    while ((brother != NULL) && (! menu_is_menubar(brother->name)))
	    brother = brother->next;

    if (STRNCMP(menu->dname, "-SEP", 4) == 0 || 
        STRNCMP(menu->dname, "-sep", 4) == 0)
        mac_menu_item = [NSMenuItem separatorItem];
    else
    {
        NSString *title = [[NSString alloc] initWithUTF8String: (const char *) menu->dname];
        mac_menu_item = [[NSMenuItem alloc] initWithTitle: title 
                                                   action: @selector(menuAction:)
                                            keyEquivalent: @""];
        [title release];
        [mac_menu_item setTarget: NSApp];
        [mac_menu_item setTag: (int) menu];
        alloc = 1;
        
        if (menu->actext != NULL)
        {
            NSString *tooltip = [[NSString alloc] initWithUTF8String: (const char *) menu->actext];
            
            [mac_menu_item setToolTip: tooltip];
            [tooltip release];
        }
    }
    menu->item_handle = (void *) mac_menu_item;
                                      
    parent = menu->parent;
    if (parent == NULL)
        parent_menu = [NSApp mainMenu];
    else
        parent_menu = (NSMenu *) parent->menu_handle;

    /* If index == -1, means in parent menu we cannot find 
     * this menu item, must be something wrong, but we still
     * need to handle this gracefully */
    if (brother != NULL && 
        (item = (NSMenuItem *) brother->item_handle) != NULL &&
        (index = [parent_menu indexOfItem: item]) != -1)
        [parent_menu insertItem: mac_menu_item
                        atIndex: index];
    else
        [parent_menu addItem: mac_menu_item];
    
    if (alloc)
        [mac_menu_item release];
    return mac_menu_item;
}

void gui_mch_add_menu(vimmenu_T *menu, int idx)
{
    // fprintf(stderr, "gui_mch_add_menu: %s, %d\n", menu->dname, idx);    
    if (! menu_is_menubar(menu->name))
        return;

    NSString *title = [[NSString alloc] initWithUTF8String: (const char *) menu->dname];
    NSMenu *mac_menu = [[NSMenu alloc] initWithTitle: title];
    [title release];
    NSMenuItem *mac_menu_item = insert_menu_item(menu);

    [mac_menu_item setSubmenu: mac_menu];
    menu->menu_handle = (void *) mac_menu;
    [mac_menu release];
}

void gui_mch_add_menu_item(vimmenu_T *menu, int idx)
{
    // fprintf(stderr, "gui_mch_add_menu_item: %s, %d\n", menu->dname, idx);
    
    insert_menu_item(menu);
}

void gui_mch_destroy_menu(vimmenu_T *menu)
{
    NSMenu *parent_menu;

    if (menu == NULL)
        return;
        
    if (menu->parent == NULL)
        parent_menu = [NSApp mainMenu];
    else
        parent_menu = (NSMenu *) menu->parent->menu_handle;
        
    [parent_menu removeItem: (NSMenuItem *) menu->item_handle];
    menu->item_handle = NULL;
}

void gui_mch_draw_menubar()
{
}

void gui_mch_menu_grey(vimmenu_T *menu, int grey)
{
    NSMenuItem *item;
    
    return;

    if (menu == NULL)
        return;
    
    item = (NSMenuItem *) menu->item_handle;
    [item setEnabled: grey ? NO : YES];
}

void gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
{
    /* There's no hidden mode on MacOS */
    gui_mch_menu_grey(menu, hidden);
}

void gui_mch_show_popupmenu(vimmenu_T *menu)
{
}

void gui_mch_enable_menu(int flag)
{
    /* menu is always active */
}

void gui_mch_set_menu_pos(int x, int y, int w, int h)
{
    /* menu position is fixed, always at the top */
}

/* Menu Handling }}} */

/* Dialog related {{{ */

char_u *gui_mch_browse(
    int saving,
    char_u *title,
    char_u *dflt,
    char_u *ext,
    char_u *initdir,
    char_u *filter)
{
    return NULL;
}

int gui_mch_dialog(
    int     type,
    char_u  *title,
    char_u  *message,
    char_u  *buttons,
    int     dfltbutton,
    char_u  *textfield)
{
    return 1;
}

/* Dialog related }}} */

/* Color related {{{ */

/* Colors Macros */
#define RGB(r,g,b)  ((r) << 16) + ((g) << 8) + (b)
#define Red(c)      ((c & 0x00FF0000) >> 16)
#define Green(c)    ((c & 0x0000FF00) >>  8)
#define Blue(c)     ((c & 0x000000FF) >>  0)

long_u gui_mch_get_rgb(guicolor_T pixel)
{
    return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
}

void gui_mch_new_colors()
{
}

guicolor_T gui_mch_get_color(char_u *name)
{
    typedef struct guicolor_tTable
    {
        char        *name;
        guicolor_T  color;
    } guicolor_tTable;

    /*
     * The comment at the end of each line is the source
     * (Mac, Window, Unix) and the number is the unix rgb.txt value
     */
    static guicolor_tTable table[] =
    {
    { "Black",      RGB(0x00, 0x00, 0x00) },
    { "darkgray",   RGB(0x80, 0x80, 0x80) }, /*W*/
    { "darkgrey",   RGB(0x80, 0x80, 0x80) }, /*W*/
    { "Gray",       RGB(0xC0, 0xC0, 0xC0) }, /*W*/
    { "Grey",       RGB(0xC0, 0xC0, 0xC0) }, /*W*/
    { "lightgray",  RGB(0xE0, 0xE0, 0xE0) }, /*W*/
    { "lightgrey",  RGB(0xE0, 0xE0, 0xE0) }, /*W*/
    { "gray10",     RGB(0x1A, 0x1A, 0x1A) }, /*W*/
    { "grey10",     RGB(0x1A, 0x1A, 0x1A) }, /*W*/
    { "gray20",     RGB(0x33, 0x33, 0x33) }, /*W*/
    { "grey20",     RGB(0x33, 0x33, 0x33) }, /*W*/
    { "gray30",     RGB(0x4D, 0x4D, 0x4D) }, /*W*/
    { "grey30",     RGB(0x4D, 0x4D, 0x4D) }, /*W*/
    { "gray40",     RGB(0x66, 0x66, 0x66) }, /*W*/
    { "grey40",     RGB(0x66, 0x66, 0x66) }, /*W*/
    { "gray50",     RGB(0x7F, 0x7F, 0x7F) }, /*W*/
    { "grey50",     RGB(0x7F, 0x7F, 0x7F) }, /*W*/
    { "gray60",     RGB(0x99, 0x99, 0x99) }, /*W*/
    { "grey60",     RGB(0x99, 0x99, 0x99) }, /*W*/
    { "gray70",     RGB(0xB3, 0xB3, 0xB3) }, /*W*/
    { "grey70",     RGB(0xB3, 0xB3, 0xB3) }, /*W*/
    { "gray80",     RGB(0xCC, 0xCC, 0xCC) }, /*W*/
    { "grey80",     RGB(0xCC, 0xCC, 0xCC) }, /*W*/
    { "gray90",     RGB(0xE5, 0xE5, 0xE5) }, /*W*/
    { "grey90",     RGB(0xE5, 0xE5, 0xE5) }, /*W*/
    { "white",      RGB(0xFF, 0xFF, 0xFF) },
    { "darkred",    RGB(0x80, 0x00, 0x00) }, /*W*/
    { "red",        RGB(0xDD, 0x08, 0x06) }, /*M*/
    { "lightred",   RGB(0xFF, 0xA0, 0xA0) }, /*W*/
    { "DarkBlue",   RGB(0x00, 0x00, 0x80) }, /*W*/
    { "Blue",       RGB(0x00, 0x00, 0xD4) }, /*M*/
    { "lightblue",  RGB(0xA0, 0xA0, 0xFF) }, /*W*/
    { "DarkGreen",  RGB(0x00, 0x80, 0x00) }, /*W*/
    { "Green",      RGB(0x00, 0x64, 0x11) }, /*M*/
    { "lightgreen", RGB(0xA0, 0xFF, 0xA0) }, /*W*/
    { "DarkCyan",   RGB(0x00, 0x80, 0x80) }, /*W ?0x307D7E */
    { "cyan",       RGB(0x02, 0xAB, 0xEA) }, /*M*/
    { "lightcyan",  RGB(0xA0, 0xFF, 0xFF) }, /*W*/
    { "darkmagenta",RGB(0x80, 0x00, 0x80) }, /*W*/
    { "magenta",    RGB(0xF2, 0x08, 0x84) }, /*M*/
    { "lightmagenta",RGB(0xF0, 0xA0, 0xF0) }, /*W*/
    { "brown",      RGB(0x80, 0x40, 0x40) }, /*W*/
    { "yellow",     RGB(0xFC, 0xF3, 0x05) }, /*M*/
    { "lightyellow",RGB(0xFF, 0xFF, 0xA0) }, /*M*/
    { "darkyellow", RGB(0xBB, 0xBB, 0x00) }, /*U*/
    { "SeaGreen",   RGB(0x2E, 0x8B, 0x57) }, /*W 0x4E8975 */
    { "orange",     RGB(0xFC, 0x80, 0x00) }, /*W 0xF87A17 */
    { "Purple",     RGB(0xA0, 0x20, 0xF0) }, /*W 0x8e35e5 */
    { "SlateBlue",  RGB(0x6A, 0x5A, 0xCD) }, /*W 0x737CA1 */
    { "Violet",     RGB(0x8D, 0x38, 0xC9) }, /*U*/
    };

    int r, g, b;
    int i;

    if (name[0] == '#' && strlen((char *) name) == 7)
    {
        /* Name is in "#rrggbb" format */
        r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
        g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
        b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
        if (r < 0 || g < 0 || b < 0)
            return INVALCOLOR;
        return RGB(r, g, b);
    }
    else
    {
        /* Check if the name is one of the colors we know */
        for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
            if (STRICMP(name, table[i].name) == 0)
            return table[i].color;
    }

    return INVALCOLOR;
}

void gui_mch_set_fg_color(guicolor_T color)
{
    [NSApp setForegroundColor: (int) color];
}

void gui_mch_set_bg_color(guicolor_T color)
{
    [NSApp setBackgroundColor: (int) color];
}

void gui_mch_set_sp_color(guicolor_T color)
{
}

/* Color related }}} */

/* Drawing related {{{ */

void gui_mch_flush()
{
    // fprintf(stderr, "gui_mch_flush\n");
    // redraw();
}

static inline CGRect CGRectFromNSRect(NSRect nsRect) { return *(CGRect*)&nsRect; }

void gui_mch_invert_rectangle(int r, int c, int nr, int nc)
{
    fprintf(stderr, "gui_mch_invert_rectangle\n");
}

void gui_mch_flash(int msec)
{
    fprintf(stderr, "gui_mch_flash\n");
}

void gui_mch_clear_all()
{
    fprintf(stderr, "gui_mch_clear_all\n");
    
    [NSApp clearAll];
}

void gui_mch_clear_block(int row1, int col1, int row2, int col2)
{
    [NSApp clearBlockFromRow: row1 column: col1
                       toRow: row2 column: col2];
}

void gui_mch_delete_lines(int row, int num_lines)
{
    [NSApp deleteLinesFromRow: row 
                        count: num_lines
                 scrollBottom: gui.scroll_region_bot
                         left: gui.scroll_region_left
                        right: gui.scroll_region_right];
}

void gui_mch_insert_lines(int row, int num_lines)
{
    [NSApp insertLinesFromRow: row 
                        count: num_lines
                 scrollBottom: gui.scroll_region_bot
                         left: gui.scroll_region_left
                        right: gui.scroll_region_right];
}

void gui_mch_draw_hollow_cursor(guicolor_T color)
{
    fprintf(stderr, "gui_mch_draw_hollow_cursor\n");
}

void gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
{
}

void gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
{
    [NSApp replaceString: (char*) s 
                  length: len
                     row: row 
                  column: col 
                   flags: flags];
}

/* Drawing related }}} */

/* Clipboard related {{{ */

void clip_mch_lose_selection(VimClipboard *cbd)
{
}

int clip_mch_own_selection(VimClipboard *cbd)
{
    return OK;
}

void clip_mch_set_selection(VimClipboard *cbd)
{
}

void clip_mch_request_selection(VimClipboard *cbd)
{
}

/* Clipboard related }}} */

/* Scrollbar related {{{ */

void gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
{
    fprintf(stderr, "gui_mch_create_scrollbar: ident = %ld, "
                    "type = %d, value = %ld, size = %ld, "
                    "max = %ld, top = %d, height = %d, "
                    "width = %d, status_height = %d\n", 
            sb->ident, sb->type, sb->value, sb->size, sb->max, 
            sb->top, sb->height, sb->width, sb->status_height);
            
    NSScroller *scroller;

    scroller = [[NSScroller alloc] init];
    sb->scroller = (void *) scroller;
}

void gui_mch_destroy_scrollbar(scrollbar_T *sb)
{
    NSScroller *scroller = (NSScroller *) sb->scroller;
    
    if (scroller != nil)
        [scroller release];
        
    sb->scroller = NULL;
}

void gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
{
    fprintf(stderr, "enable scrollbar: %ld - %d\n", 
            sb->ident, flag);
}

void gui_mch_set_scrollbar_pos(
    scrollbar_T *sb,
    int x,
    int y,
    int w,
    int h)
{
    fprintf(stderr, "set scrollbar pos: %ld, (%d, %d, %d, %d)\n", 
            sb->ident, x, y, w, h);
}

void gui_mch_set_scrollbar_thumb(
    scrollbar_T *sb,
    long val,
    long size,
    long max)
{
}

/* Scrollbar related }}} */

/* Mouse related {{{ */

void gui_mch_getmouse(int *x, int *y)
{
}

void gui_mch_setmouse(int x, int y)
{
}

/* Mouse related }}} */

/* Cursor blinking stuff {{{ */

void gui_mch_set_blinking(long wait, long on, long off)
{
}

void gui_mch_stop_blink()
{
}

void gui_mch_start_blink()
{
}

/* Cursor blinking stuff }}} */

/* GUI tab stuff {{{ */

void gui_mch_set_curtab(int nr)
{
}

void gui_mch_show_tabline(int showit)
{
}

int gui_mch_showing_tabline() { return 0; }

void gui_mch_update_tabline()
{
}

/* GUI tab stuff }}} */

/* Private Functions {{{1 */

int hex_digit(int c)
{
    if (isdigit(c))
        return c - '0';
    
    c = TOLOWER_ASC(c);
    
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    
    return -1000;
}

NSColor *NSColorFromGuiColor(guicolor_T color, float alpha)
{
    float red, green, blue;

    red   = (float) Red(color) / (float) 0xFF;
    green = (float) Green(color) / (float) 0xFF;
    blue  = (float) Blue(color) / (float) 0xFF;

    return [NSColor colorWithDeviceRed: red
                                 green: green
                                  blue: blue
                                 alpha: alpha];
}

/* Font Related Utilities 2{{{ */
    
NSFont *get_font(char_u *font_name, int size)
{
    fprintf(stderr, "get_font: %s\n", font_name);
    NSString *mac_font_name = [NSString stringWithUTF8String: (const char *) font_name];
    
    NSLog(@"fontWithName: %@, %d", mac_font_name, size);

    return [NSFont fontWithName: mac_font_name
                           size: size];
}

GuiFont find_font(char_u *font_spec)
{
    int       len = 0, size = VIM_DEFAULT_FONT_SIZE;
    NSFont   *font;
    char_u   *font_style, *p;
    char_u    font_name[VIM_MAX_FONT_NAME_LEN];
    
    fprintf(stderr, "find_font: %s\n", font_spec);

    font_style = vim_strchr(font_spec, ':');
    len = font_style - font_spec;
    
    fprintf(stderr, "len = %d\n", len);
    
    if (len < 0 || len >= VIM_MAX_FONT_NAME_LEN)
        return NOFONT;

    vim_strncpy(font_name, font_spec, len);
    font_name[len] = '\0';

    if (*font_style == ':')
    {
        p = font_style + 1;
        /* Set the values found after ':' */
        while (*p)
        {
            switch (*p++)
            {
            case 'h':
                size = points_to_pixels(p, &p);
                break;
                /*
                 * TODO: Maybe accept width and styles
                 */
            }

            while (*p == ':')
                p++;
        }
    }

    font = get_font(font_name, size); 
    if (font == nil)
    {
        /*
         * Try again, this time replacing underscores in the font name
         * with spaces (:set guifont allows the two to be used
         * interchangeably; the Font Manager doesn't).
         */
        int i, changed = FALSE;

        for (i = font_name[0]; i > 0; --i)
        {
            if (font_name[i] == '_')
            {
                font_name[i] = ' ';
                changed = TRUE;
            }
        }

        if (changed)
            font = get_font(font_name, size);
    }

    if (font == nil)
        return NOFONT;

    [font retain];
    return (long_u) font;
}

int points_to_pixels(char_u *str, char_u **end)
{
    int pixels;
    int points = 0;
    int divisor = 0;

    while (*str)
    {
        if (*str == '.' && divisor == 0)
        {
            /* Start keeping a divisor, for later */
            divisor = 1;
            continue;
        }

        if (! isdigit(*str))
            break;

        points *= 10;
        points += *str - '0';
        divisor *= 10;

        ++str;
    }

    if (divisor == 0)
        divisor = 1;

    pixels = points / divisor;
    *end = str;
    return pixels;
}

/* Font Related Utilities 2}}} */

/* Private Functions 1}}} */
